package org.act.bpel2automata.popup.actions;

import java.awt.Dimension;
import java.awt.Point;
import java.util.LinkedList;

import org.act.BPEL2Automata.automata.AutomataImp;
import org.act.BPEL2Automata.automata.SubAutomataImp;
import org.act.BPEL2Automata.automata.module.Message;
import org.act.BPEL2Automata.automata.module.State;
import org.act.BPEL2Automata.automata.module.StatePair;
import org.act.BPEL2Automata.graphics.AutomatonGraph;
import org.act.BPEL2Automata.graphics.Convertor;
import org.act.BPEL2Automata.graphics.GraphPainterFactory;
import org.act.BPEL2Automata.graphics.LayoutAlgorithm;
import org.act.BPEL2Automata.graphics.LayoutAlgorithmFactory;
import org.act.BPEL2Automata.graphics.model.Automaton;
import org.act.BPEL2Automata.transformer.Transformer;
import org.act.bpel2automata.views.BisimulationView;
import org.act.bpel2automata.views.GraphView;
import org.act.bpel2automata.views.PropertyView;
import org.act.bpel2automata.views.model.EntityElement;
import org.act.bpel2automata.views.model.EntityFactory;
import org.eclipse.core.resources.IFile;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.ScrollBar;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IObjectActionDelegate;
import org.eclipse.ui.IViewPart;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.ide.IDE;

public class BPEL2Automata implements IObjectActionDelegate {
	public static final String VIEW_ID_PROPERTY = "org.act.bpel2automata.views.PropertyView";
	public static final String VIEW_ID_GRAPH = "org.act.bpel2automata.views.GraphView";
	public static final String VIEW_ID_BISIMULATION = "org.act.bpel2automata.views.BisimulationView";
	public static final String EDITOR_ID_BPEL = "org.act.bpel2automata.editors.BPELEditor";
	
	private static ISelectionChangedListener selectionChangedListener = null;
	private static PaintListener onePaintListener = null;
	private static PaintListener twoPaintListener = null;
	
	private Shell shell;
	private ISelection selection;
	private IWorkbenchPart targetPart;
	
	private State state1 = null;
	private State state2 = null;
	private Canvas gCanvas = null;
	
	/**
	 * Constructor for Action1.
	 */
	public BPEL2Automata() {
		super();
	}

	/**
	 * @see IObjectActionDelegate#setActivePart(IAction, IWorkbenchPart)
	 */
	public void setActivePart(IAction action, IWorkbenchPart targetPart) {
		shell = targetPart.getSite().getShell();
		this.targetPart = targetPart;
	}

	/**
	 * @see IActionDelegate#run(IAction)
	 */
	public void run(IAction action) {
		SubAutomataImp automaton1 = null;
		SubAutomataImp automaton2 = null;
		
		//selection is not NULL
		if(!(selection instanceof IStructuredSelection))
		{
			return;
		}
		
		IStructuredSelection struct = (IStructuredSelection) (selection);
		
		//handle 1 or 2 bpel file(s).
		if(struct.size() > 2){
			MessageDialog.openError(shell, "Bpel2automata Plug-in", "2 BPEL files at most can be selected!");
			
			return;
		}
		
		
		//selected file(s).1 or 2 files.
		Object[] files = struct.toArray();
		
		IWorkbenchPage page = this.targetPart.getSite().getPage();
		
		//select one bpel file
		if(files.length == 1){
			IFile file = (IFile)files[0];
			
			try {
				Transformer tf = new Transformer();
				
				SubAutomataImp automata = tf.transform(file.getLocation().toString());
				
				automaton1 = automata;
				
				final Automaton auto = Convertor.convert(AutomataImp.toAutomataImp(automata));
				
				//System.err.println(auto);
				
				//show graph
				//PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().showView("org.act.bpel2automata.views.GraphView");
				//hide bisimulation view,as it's not necessary if you select only one bpel file.
				page.showView(BPEL2Automata.VIEW_ID_BISIMULATION);
				page.hideView(page.findView(BPEL2Automata.VIEW_ID_BISIMULATION));
				
				page.showView(BPEL2Automata.VIEW_ID_GRAPH);
				page.hideView(page.findView(BPEL2Automata.VIEW_ID_GRAPH));
				
				page.showView(BPEL2Automata.VIEW_ID_GRAPH);
				
				IViewPart view = page.findView(BPEL2Automata.VIEW_ID_GRAPH);
				
				//System.out.println("info@BPEL2Automata> view  = " + view);
				
				if(view instanceof GraphView){
					GraphView gView = (GraphView)view;
					final Canvas canvas = gView.getCanvas();
					
					AutomatonGraph graph = LayoutAlgorithmFactory.getAutomatonGraph(LayoutAlgorithmFactory.TREE_DEGREE, auto);
					
					LayoutAlgorithm layout = LayoutAlgorithmFactory.getLayoutAlgorithm(LayoutAlgorithmFactory.TREE_DEGREE, 
							new Dimension(GraphPainterFactory.VERTEX_SPACE_SIZE_DEFAULT,GraphPainterFactory.VERTEX_SPACE_SIZE_DEFAULT), 
							new Dimension(GraphPainterFactory.VERTEX_SPACING_DEFAULT, GraphPainterFactory.VERTEX_SPACING_DEFAULT),
							GraphPainterFactory.VERTEX_BUFFER_SIZE_DEFAULT);
					layout.layout(graph, null);
					graph.moveAutomatonStates();
					
					//System.out.println(auto);
					auto.adjust(Automaton.ADJUST_TYPE_REVERSE, 0);
					//auto.adjust(Automaton.ADJUST_TYPE_HORIZONTAL_RIGHT,100);
					
					//if canvas has one PaintListener,remove it.
					if(onePaintListener != null){
						canvas.removePaintListener(onePaintListener);
						onePaintListener = null;
					}
					
					canvas.addPaintListener(onePaintListener = new PaintListener() {
					    public void paintControl(PaintEvent event) {
					    	GC gc = event.gc;
					    	GraphPainterFactory gpf = new GraphPainterFactory(gc);
							
							gpf.draw(auto);
							gc.dispose();
					    }
					});
					
					/*ADDED@2010-06-02*/
					final Point origin = new Point(0, 0);
					final ScrollBar hBar = canvas.getHorizontalBar();
					hBar.addListener(SWT.Selection, new Listener() {
					      public void handleEvent(Event e) {
					        int hSelection = hBar.getSelection();
					        int destX = -hSelection - origin.x;
					        
					        canvas.scroll(destX, 0, 0, 0, GraphPainterFactory.CANVAS_SIZE_DEFAULT, GraphPainterFactory.CANVAS_SIZE_DEFAULT, false);
					        origin.x = -hSelection;
					      }
					    });
					
					final ScrollBar vBar = canvas.getVerticalBar();
				    vBar.addListener(SWT.Selection, new Listener() {
				      public void handleEvent(Event e) {
				        int vSelection = vBar.getSelection();
				        int destY = -vSelection - origin.y;
				        
				        canvas.scroll(0, destY, 0, 0, GraphPainterFactory.CANVAS_SIZE_DEFAULT, GraphPainterFactory.CANVAS_SIZE_DEFAULT, false);
				        canvas.update();
				        origin.y = -vSelection;
				        
				      }
				    });
					/*END of ADDING*/
				}
				
			} catch (PartInitException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		//select two bpel files
		else
		if(files.length == 2){
			IFile file1 = (IFile)files[0];
			IFile file2 = (IFile)files[1];
			
			try {
				Transformer tf = new Transformer();
				
				final SubAutomataImp automata1 = tf.transform(file1.getLocation().toString());
				final SubAutomataImp automata2 = tf.transform(file2.getLocation().toString());
				
				automaton1 = automata1;
				automaton2 = automata2;
				
				final Automaton auto1 = Convertor.convert(AutomataImp.toAutomataImp(automata1));
				final Automaton auto2 = Convertor.convert(AutomataImp.toAutomataImp(automata2));
				
				page.showView(BPEL2Automata.VIEW_ID_GRAPH);
				page.hideView(page.findView(BPEL2Automata.VIEW_ID_GRAPH));
				
				page.showView(BPEL2Automata.VIEW_ID_GRAPH);
				
				IViewPart view = page.findView(BPEL2Automata.VIEW_ID_GRAPH);
				
				if(view instanceof GraphView){
					GraphView gView = (GraphView)view;
					Canvas canvas = gView.getCanvas();
					gCanvas = canvas;
					
					AutomatonGraph graph1 = LayoutAlgorithmFactory.getAutomatonGraph(LayoutAlgorithmFactory.TREE_DEGREE, auto1);
					AutomatonGraph graph2 = LayoutAlgorithmFactory.getAutomatonGraph(LayoutAlgorithmFactory.TREE_DEGREE, auto2);
					
					LayoutAlgorithm layout = LayoutAlgorithmFactory.getLayoutAlgorithm(LayoutAlgorithmFactory.TREE_DEGREE, 
							new Dimension(GraphPainterFactory.VERTEX_SPACE_SIZE_DEFAULT,GraphPainterFactory.VERTEX_SPACE_SIZE_DEFAULT), 
							new Dimension(GraphPainterFactory.VERTEX_SPACING_DEFAULT, GraphPainterFactory.VERTEX_SPACING_DEFAULT),
							GraphPainterFactory.VERTEX_BUFFER_SIZE_DEFAULT);
					
					layout.layout(graph1, null);
					layout.layout(graph2, null);
					
					graph1.moveAutomatonStates();
					graph2.moveAutomatonStates();
					
					auto1.adjust(Automaton.ADJUST_TYPE_REVERSE, 0);
					auto2.adjust(Automaton.ADJUST_TYPE_REVERSE, 0);
					
					auto2.adjust(Automaton.ADJUST_TYPE_HORIZONTAL_RIGHT, auto1.getXSpan() + Automaton.ADJUST_SPACING);
					
					
					/*
					if(canvas.getListeners(SWT.Paint).length == 1){
						canvas.removePaintListener((PaintListener)canvas.getListeners(SWT.Paint)[0]);
					}
					*/
					//if canvas has one PaintListener,remove it.
					if(twoPaintListener != null){
						canvas.removePaintListener(twoPaintListener);
						twoPaintListener = null;
					}
					
					canvas.addPaintListener(twoPaintListener = new PaintListener() {
					    public void paintControl(PaintEvent event) {
					    	GC gc = event.gc;
					    	
					    	GraphPainterFactory gpf = new GraphPainterFactory(gc);
					    	
							gpf.draw(auto1,state1);
							gpf.draw(auto2,state2);
							gc.dispose();
					    }
					});
					
				}
				
				//TODO
				//show Bisimulation View
				AutomataImp.clearCache();
				boolean flag = AutomataImp.calBisimulation(AutomataImp.toAutomataImp(automata1), AutomataImp.toAutomataImp(automata2));
				LinkedList<StatePair> sp = AutomataImp.bisimulationResult;
				
				
				if(flag){
					MessageDialog.openInformation(shell, "Bpel2automata Plug-in", "These two bpel processes can bi-simulate!");
				}
				else{
					MessageDialog.openInformation(shell, "Bpel2automata Plug-in", "These two bpel processes can NOT bi-simulate!");
				}
				
				
				page.showView(BPEL2Automata.VIEW_ID_BISIMULATION);
				page.hideView(page.findView(BPEL2Automata.VIEW_ID_BISIMULATION));
				
				page.showView(BPEL2Automata.VIEW_ID_BISIMULATION);
				view = page.findView(BPEL2Automata.VIEW_ID_BISIMULATION);
				
				if(view instanceof BisimulationView){
					TreeViewer tv = ((BisimulationView)view).getTreeViewer();
					
					tv.addSelectionChangedListener(new ISelectionChangedListener() {
						   public void selectionChanged(SelectionChangedEvent event) {
							   if(event.getSelection().isEmpty()) {
						           return;
						       }
						       
						       if(event.getSelection() instanceof IStructuredSelection) {
						           IStructuredSelection selection = (IStructuredSelection)event.getSelection();
						           
						           EntityElement e = (EntityElement)selection.getFirstElement();
						           
						           if(e.getFrom() instanceof StatePair){
						        	   StatePair sp = (StatePair)e.getFrom();
						        	   
						        	   if(automata1.getStateSet().contains(sp.getStateX())){
						        		   state1 = sp.getStateX();
							        	   state2 = sp.getStateY();
						        	   }
						        	   else{
						        		   state1 = sp.getStateY();
							        	   state2 = sp.getStateX();
						        	   }
						        	   
						        	   gCanvas.redraw();
						           }
						       }
						   }
						});
					
					tv.setInput(EntityFactory.genElements(sp));
				}
				
				
				
			} catch (PartInitException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
		
		//show properties:messages and states
		try {
			page.showView(BPEL2Automata.VIEW_ID_PROPERTY);
			page.hideView(page.findView(BPEL2Automata.VIEW_ID_PROPERTY));
			
			page.showView(BPEL2Automata.VIEW_ID_PROPERTY);
			IViewPart view = page.findView(BPEL2Automata.VIEW_ID_PROPERTY);
			
			if(view instanceof PropertyView){
				TreeViewer tv = ((PropertyView)view).getTreeViewer();
				
				//if TreeViewer has a listener, remove it.
				if(selectionChangedListener != null){
					tv.removeSelectionChangedListener(selectionChangedListener);
					selectionChangedListener = null;
				}
				
				tv.addSelectionChangedListener(selectionChangedListener = new ISelectionChangedListener() {
					   public void selectionChanged(SelectionChangedEvent event) {
					       // if the selection is empty clear the label
					       if(event.getSelection().isEmpty()) {
					           return;
					       }
					       
					       if(event.getSelection() instanceof IStructuredSelection) {
					           IStructuredSelection selection = (IStructuredSelection)event.getSelection();
					           
					           //ONLY handle selecting one single file.
					           if(selection.size() == 1){
					        	   EntityElement e = (EntityElement)selection.getFirstElement();
					        	   
					        	   if(e.getFrom() instanceof State){
					        		   State state = (State)e.getFrom();

					        		   MessageDialog.openInformation(shell, "Bpel2automata Plug-in", e.getName()
					        				   + state);
					        		   
					        	   }
					        	   else
					        	   if(e.getFrom() instanceof Message){
					        		   Message mes = (Message)e.getFrom();
					        		   
					        		   MessageDialog.openInformation(shell, "Bpel2automata Plug-in", e.getName()
					        				   + "\nPartnerLink:" + mes.getPartnerLink().getName()
					        				   + "\nPortType:" + mes.getPortType().getName()
					        				   + "\nOperation:" + mes.getOperation().getName()
					        				   + "\nMessageType:" + mes.getMessageType().getName()
					        				   + "\nDirection:" + mes.getDirection());
						           }
					           }
					       }
					   }
					});
				
				tv.setInput(EntityFactory.genElements(automaton1,automaton2));
				
				tv.refresh();
			}
			
		} catch (PartInitException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		
		
		
		//open bpel file(s) with BPEL editor.
		for(int i = 0; i < files.length; i++){
			IFile file = (IFile)files[i];
			
			try {
				IDE.openEditor(page, file, BPEL2Automata.EDITOR_ID_BPEL);
			} catch (PartInitException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

	/**
	 * @see IActionDelegate#selectionChanged(IAction, ISelection)
	 */
	public void selectionChanged(IAction action, ISelection selection) {
		this.selection = selection;
	}

}
